/*
 * Wazuh Vulnerability Scanner - Unit Tests
 * Copyright (C) 2015, Wazuh Inc.
 * March 5, 2024.
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation.
 */

#include "scanOsAlertDetailsBuilder_test.hpp"
#include "../../../../shared_modules/utils/flatbuffers/include/rsync_generated.h"
#include "../../../../shared_modules/utils/flatbuffers/include/rsync_schema.h"
#include "../../../../shared_modules/utils/flatbuffers/include/syscollector_deltas_generated.h"
#include "../../../../shared_modules/utils/flatbuffers/include/syscollector_deltas_schema.h"
#include "../scanOrchestrator/scanOsAlertDetailsBuilder.hpp"
#include "MockDatabaseFeedManager.hpp"
#include "MockGlobalData.hpp"
#include "MockOsDataCache.hpp"
#include "MockRemediationDataCache.hpp"
#include "TrampolineGlobalData.hpp"
#include "TrampolineOsDataCache.hpp"
#include "TrampolineRemediationDataCache.hpp"
#include "flatbuffers/flatbuffer_builder.h"
#include "flatbuffers/flatbuffers.h"
#include "flatbuffers/idl.h"
#include "json.hpp"
#include "vulnerabilityDescription_schema.h"
#include <unistd.h>

using ::testing::_;
using ::testing::HasSubstr;
using ::testing::ThrowsMessage;

namespace NSScanOsAlertDetailsBuilderTest
{
    constexpr auto TEST_DESCRIPTION_DATABASE_PATH {"queue/vd/descriptions"};

    const std::string OS_SCAN_MSG =
        R"({
                "agent_info": {
                    "agent_id": "002"
                },
                "data_type": "state",
                "data": {
                    "attributes_type": "syscollector_osinfo",
                    "attributes": {
                        "architecture": "x86_64",
                        "checksum": "1691178971959743855",
                        "hostname":"fd9b83c25f30",
                        "os_major":"10",
                        "os_minor":"0",
                        "os_build":"19045.4046",
                        "os_name":"Microsoft Windows 10 Pro",
                        "os_display_version":"22H2",
                        "os_platform":"windows",
                        "os_version":"10.0.19045.4043",
                        "scan_time":"2023/08/04 19:56:11"
                    }
                }
            })";

    const std::string CVEID {"CVE-2024-1234"};

    const std::string CTI_CVE_URL {"https://cti.wazuh.com/vulnerabilities/cves/CVE-2024-1234"};

    const std::string DESCRIPTIONS_COLUMN_DEFAULT {"descriptions_nvd"};

    const nlohmann::json ADP_DESCRIPTIONS =
        R"#(
        {
            "adp_descriptions": {
    "alas": {
      "adp": "Amazon Linux Security Center",
      "description": "nvd",
      "cvss": "alas"
    },
    "alma": {
      "adp": "Alma Linux Security Oval",
      "description": "alma",
      "cvss": "alma"
    },
    "arch": {
      "adp": "Arch Linux Security Tracker",
      "description": "nvd",
      "cvss": "nvd"
    },
    "debian": {
      "adp": "Debian Security Tracker",
      "description": "debian",
      "cvss": "nvd"
    },
    "oracle": {
      "adp": "Oracle Linux Security",
      "description": "nvd",
      "cvss": "oracle"
    },
    "npm": {
      "adp": "Open Source Vulnerabilities",
      "description": "npm",
      "cvss": "npm"
    },
    "nvd": {
      "adp": "National Vulnerability Database",
      "description": "nvd",
      "cvss": "nvd"
    },
    "pypi": {
      "adp": "Open Source Vulnerabilities",
      "description": "pypi",
      "cvss": "pypi"
    },
    "redhat": {
      "adp": "Red Hat CVE Database",
      "description": "redhat",
      "cvss": "redhat"
    },
    "rocky": {
      "adp": "Rocky Enterprise Product Errata",
      "description": "rocky",
      "cvss": "rocky"
    },
    "suse": {
      "adp": "SUSE CVE Database",
      "description": "suse",
      "cvss": "suse"
    },
    "opensuse": {
      "adp": "SUSE CVE Database",
      "description": "suse",
      "cvss": "suse"
    },
    "canonical": {
      "adp": "Canonical Security Tracker",
      "description": "canonical",
      "cvss": "canonical"
    },
    "homebrew": {
      "adp": "Homebrew Security Audit",
      "description": "homebrew",
      "cvss": "nvd"
    }
  }
        }
    )#"_json;

    // Helpers

    const Os osData {.hostName = "osdata_hostname",
                     .architecture = "osdata_architecture",
                     .name = "osdata_name",
                     .codeName = "osdata_codeName",
                     .majorVersion = "osdata_majorVersion",
                     .minorVersion = "osdata_minorVersion",
                     .patch = "osdata_patch",
                     .build = "osdata_build",
                     .platform = "osdata_platform",
                     .version = "osdata_version",
                     .release = "osdata_release",
                     .displayVersion = "osdata_displayVersion",
                     .sysName = "osdata_sysName",
                     .kernelVersion = "osdata_kernelVersion",
                     .kernelRelease = "osdata_kernelRelease"};

    void expectOsData()
    {
        spOsDataCacheMock = std::make_shared<MockOsDataCache>();
        EXPECT_CALL(*spOsDataCacheMock, setOsData(_, _)).Times(1);
    }

} // namespace NSScanOsAlertDetailsBuilderTest

// Shared pointers definitions
std::shared_ptr<MockOsDataCache> spOsDataCacheMock;
std::shared_ptr<MockRemediationDataCache> spRemediationDataCacheMock;

using namespace NSScanOsAlertDetailsBuilderTest;

void ScanOsAlertDetailsBuilderTest::SetUp()
{
    std::filesystem::create_directories("queue/vd");

    // Policy manager initialization.
    const auto& configJson {nlohmann::json::parse(R"({
    "vulnerability-detection": {
        "enabled": "yes",
        "index-status": "yes",
        "cti-url": "cti-url.com"
    },
    "osdataLRUSize":1000,
    "clusterName":"cluster01",
    "clusterEnabled":false
    })")};
    PolicyManager::instance().initialize(configJson);
}

void ScanOsAlertDetailsBuilderTest::TearDown()
{
    spOsDataCacheMock.reset();
    spGlobalDataMock.reset();
    spRemediationDataCacheMock.reset();
    PolicyManager::instance().teardown();
    std::filesystem::remove_all("queue/vd");
}

TEST_F(ScanOsAlertDetailsBuilderTest, TestSuccessfulScanOsAlertAffects)
{
    flatbuffers::FlatBufferBuilder fbBuilder;
    auto vulnerabilityDescriptionData =
        NSVulnerabilityScanner::CreateVulnerabilityDescriptionDirect(fbBuilder,
                                                                     "accessComplexity_test_string",
                                                                     "assignerShortName_test_string",
                                                                     "attackVector_test_string",
                                                                     "authentication_test_string",
                                                                     "availabilityImpact_test_string",
                                                                     "classification_test_string",
                                                                     "confidentialityImpact_test_string",
                                                                     "cweId_test_string",
                                                                     "datePublished_test_string",
                                                                     "dateUpdated_test_string",
                                                                     "description_test_string",
                                                                     "integrityImpact_test_string",
                                                                     "privilegesRequired_test_string",
                                                                     "reference_test_string",
                                                                     "scope_test_string",
                                                                     8.3,
                                                                     "3",
                                                                     "severity_test_string",
                                                                     "userInteraction_test_string");
    fbBuilder.Finish(vulnerabilityDescriptionData);

    auto dbWrapper = std::make_unique<Utils::RocksDBWrapper>(TEST_DESCRIPTION_DATABASE_PATH);
    rocksdb::Slice dbValue(reinterpret_cast<const char*>(fbBuilder.GetBufferPointer()), fbBuilder.GetSize());
    if (!dbWrapper->columnExists(DESCRIPTIONS_COLUMN_DEFAULT))
    {
        dbWrapper->createColumn(DESCRIPTIONS_COLUMN_DEFAULT);
    }
    dbWrapper->put(CVEID, dbValue, DESCRIPTIONS_COLUMN_DEFAULT);

    expectOsData();

    spRemediationDataCacheMock = std::make_shared<MockRemediationDataCache>();
    EXPECT_CALL(*spRemediationDataCacheMock, getRemediationData(_)).WillRepeatedly(testing::Return(Remediation {}));

    auto spDatabaseFeedManagerMock = std::make_shared<MockDatabaseFeedManager>();
    EXPECT_CALL(*spDatabaseFeedManagerMock, getVulnerabilityDescriptiveInformation(_, _, _))
        .WillRepeatedly(testing::Invoke(
            [&](const std::string& cveId,
                const std::string& shortName,
                FlatbufferDataPair<NSVulnerabilityScanner::VulnerabilityDescription>& resultContainer)
            {
                dbWrapper->get(std::string(cveId), resultContainer.slice, DESCRIPTIONS_COLUMN_DEFAULT);
                if (flatbuffers::Verifier verifier(reinterpret_cast<const uint8_t*>(resultContainer.slice.data()),
                                                   resultContainer.slice.size());
                    NSVulnerabilityScanner::VerifyVulnerabilityDescriptionBuffer(verifier) == false)
                {
                    resultContainer.data = nullptr;
                    return false;
                }
                resultContainer.data = const_cast<NSVulnerabilityScanner::VulnerabilityDescription*>(
                    NSVulnerabilityScanner::GetVulnerabilityDescription(resultContainer.slice.data()));
                return true;
            }));

    spGlobalDataMock = std::make_shared<MockGlobalData>();
    EXPECT_CALL(*spGlobalDataMock, vendorMaps()).WillRepeatedly(testing::ReturnRef(ADP_DESCRIPTIONS));

    flatbuffers::Parser parser;
    ASSERT_TRUE(parser.Parse(rsync_SCHEMA));
    ASSERT_TRUE(parser.Parse(OS_SCAN_MSG.c_str()));
    uint8_t* buffer = parser.builder_.GetBufferPointer();
    std::variant<const SyscollectorDeltas::Delta*, const Synchronization::SyncMsg*, const nlohmann::json*>
        syscollectorDelta = Synchronization::GetSyncMsg(reinterpret_cast<const char*>(buffer));
    auto scanContext =
        std::make_shared<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>(
            syscollectorDelta);
    scanContext->m_elements[CVEID] = R"({"operation":"INSERTED"})"_json;
    scanContext->m_isFirstScan = false;
    scanContext->m_matchConditions[CVEID] = {"version", MatchRuleCondition::Unknown};

    TScanOsAlertDetailsBuilder<MockDatabaseFeedManager,
                               TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>,
                               TrampolineGlobalData>
        scanOsAlertDetailsAugmentation(spDatabaseFeedManagerMock);

    EXPECT_NO_THROW(scanOsAlertDetailsAugmentation.handleRequest(scanContext));

    EXPECT_EQ(scanContext->m_elements.size(), 1);
    EXPECT_NE(scanContext->m_elements.find(CVEID), scanContext->m_elements.end());

    EXPECT_EQ(scanContext->m_alerts.size(), 1);
    EXPECT_NE(scanContext->m_alerts.find(CVEID), scanContext->m_alerts.end());

    auto& alert = scanContext->m_alerts[CVEID];

    std::string alertScoreVersion {"cvss3"};

    EXPECT_STREQ(alert.at("vulnerability").at("cve").get_ref<const std::string&>().c_str(), CVEID.c_str());
    EXPECT_STREQ(alert.at("vulnerability").at("scanner").at("reference").get_ref<const std::string&>().c_str(),
                 CTI_CVE_URL.c_str());

    EXPECT_DOUBLE_EQ(
        alert.at("vulnerability").at("cvss").at(alertScoreVersion).at("base_score").get_ref<const double&>(),
        Utils::floatToDoubleRound(GetVulnerabilityDescription(fbBuilder.GetBufferPointer())->scoreBase(), 2));

    EXPECT_STREQ(alert.at("vulnerability").at("enumeration").get_ref<const std::string&>().c_str(), "CVE");
    EXPECT_STREQ(alert.at("vulnerability").at("published").get_ref<const std::string&>().c_str(),
                 GetVulnerabilityDescription(fbBuilder.GetBufferPointer())->datePublished()->c_str());
    EXPECT_STREQ(alert.at("vulnerability").at("reference").get_ref<const std::string&>().c_str(),
                 GetVulnerabilityDescription(fbBuilder.GetBufferPointer())->reference()->c_str());
    EXPECT_STREQ(
        alert.at("vulnerability").at("severity").get_ref<const std::string&>().c_str(),
        Utils::toSentenceCase(GetVulnerabilityDescription(fbBuilder.GetBufferPointer())->severity()->str()).c_str());
    EXPECT_STREQ(alert.at("vulnerability").at("status").get_ref<const std::string&>().c_str(), "Active");

    EXPECT_STREQ(alert.at("vulnerability").at("title").get_ref<const std::string&>().c_str(),
                 (CVEID + " affects " + scanContext->osName().data()).c_str());
    EXPECT_STREQ(alert.at("vulnerability").at("type").get_ref<const std::string&>().c_str(), "Packages");
    EXPECT_STREQ(alert.at("vulnerability").at("updated").get_ref<const std::string&>().c_str(),
                 GetVulnerabilityDescription(fbBuilder.GetBufferPointer())->dateUpdated()->c_str());
}

TEST_F(ScanOsAlertDetailsBuilderTest, TestVulnerabilityCvss2)
{
    flatbuffers::FlatBufferBuilder fbBuilder;
    auto vulnerabilityDescriptionData =
        NSVulnerabilityScanner::CreateVulnerabilityDescriptionDirect(fbBuilder,
                                                                     "accessComplexity_test_string",
                                                                     "assignerShortName_test_string",
                                                                     "attackVector_test_string",
                                                                     "authentication_test_string",
                                                                     "availabilityImpact_test_string",
                                                                     "classification_test_string",
                                                                     "confidentialityImpact_test_string",
                                                                     "cweId_test_string",
                                                                     "datePublished_test_string",
                                                                     "dateUpdated_test_string",
                                                                     "description_test_string",
                                                                     "integrityImpact_test_string",
                                                                     "privilegesRequired_test_string",
                                                                     "reference_test_string",
                                                                     "scope_test_string",
                                                                     8.3,
                                                                     "2",
                                                                     "severity_test_string",
                                                                     "userInteraction_test_string");
    fbBuilder.Finish(vulnerabilityDescriptionData);

    auto dbWrapper = std::make_unique<Utils::RocksDBWrapper>(TEST_DESCRIPTION_DATABASE_PATH);
    rocksdb::Slice dbValue(reinterpret_cast<const char*>(fbBuilder.GetBufferPointer()), fbBuilder.GetSize());
    if (!dbWrapper->columnExists(DESCRIPTIONS_COLUMN_DEFAULT))
    {
        dbWrapper->createColumn(DESCRIPTIONS_COLUMN_DEFAULT);
    }
    dbWrapper->put(CVEID, dbValue, DESCRIPTIONS_COLUMN_DEFAULT);

    expectOsData();

    spRemediationDataCacheMock = std::make_shared<MockRemediationDataCache>();
    EXPECT_CALL(*spRemediationDataCacheMock, getRemediationData(_)).WillRepeatedly(testing::Return(Remediation {}));

    auto spDatabaseFeedManagerMock = std::make_shared<MockDatabaseFeedManager>();
    EXPECT_CALL(*spDatabaseFeedManagerMock, getVulnerabilityDescriptiveInformation(_, _, _))
        .WillRepeatedly(testing::Invoke(
            [&](const std::string& cveId,
                const std::string& shortName,
                FlatbufferDataPair<NSVulnerabilityScanner::VulnerabilityDescription>& resultContainer)
            {
                dbWrapper->get(std::string(cveId), resultContainer.slice, DESCRIPTIONS_COLUMN_DEFAULT);
                if (flatbuffers::Verifier verifier(reinterpret_cast<const uint8_t*>(resultContainer.slice.data()),
                                                   resultContainer.slice.size());
                    NSVulnerabilityScanner::VerifyVulnerabilityDescriptionBuffer(verifier) == false)
                {
                    resultContainer.data = nullptr;
                    return false;
                }
                resultContainer.data = const_cast<NSVulnerabilityScanner::VulnerabilityDescription*>(
                    NSVulnerabilityScanner::GetVulnerabilityDescription(resultContainer.slice.data()));
                return true;
            }));

    flatbuffers::Parser parser;
    ASSERT_TRUE(parser.Parse(rsync_SCHEMA));
    ASSERT_TRUE(parser.Parse(OS_SCAN_MSG.c_str()));
    uint8_t* buffer = parser.builder_.GetBufferPointer();
    std::variant<const SyscollectorDeltas::Delta*, const Synchronization::SyncMsg*, const nlohmann::json*>
        syscollectorDelta = Synchronization::GetSyncMsg(reinterpret_cast<const char*>(buffer));
    auto scanContext =
        std::make_shared<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>(
            syscollectorDelta);
    scanContext->m_elements[CVEID] = R"({"operation":"INSERTED"})"_json;
    scanContext->m_isFirstScan = false;
    scanContext->m_matchConditions[CVEID] = {"version", MatchRuleCondition::DefaultStatus};

    spGlobalDataMock = std::make_shared<MockGlobalData>();
    EXPECT_CALL(*spGlobalDataMock, vendorMaps()).WillRepeatedly(testing::ReturnRef(ADP_DESCRIPTIONS));

    TScanOsAlertDetailsBuilder<MockDatabaseFeedManager,
                               TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>,
                               TrampolineGlobalData>
        scanOsAlertDetailsAugmentation(spDatabaseFeedManagerMock);

    EXPECT_NO_THROW(scanOsAlertDetailsAugmentation.handleRequest(scanContext));

    EXPECT_EQ(scanContext->m_elements.size(), 1);
    EXPECT_NE(scanContext->m_elements.find(CVEID), scanContext->m_elements.end());

    EXPECT_EQ(scanContext->m_alerts.size(), 1);
    EXPECT_NE(scanContext->m_alerts.find(CVEID), scanContext->m_alerts.end());

    auto& alert = scanContext->m_alerts[CVEID];

    std::string alertScoreVersion {"cvss2"};

    EXPECT_STREQ(alert.at("vulnerability").at("cve").get_ref<const std::string&>().c_str(), CVEID.c_str());
    EXPECT_STREQ(alert.at("vulnerability").at("scanner").at("reference").get_ref<const std::string&>().c_str(),
                 CTI_CVE_URL.c_str());

    EXPECT_DOUBLE_EQ(
        alert.at("vulnerability").at("cvss").at(alertScoreVersion).at("base_score").get_ref<const double&>(),
        Utils::floatToDoubleRound(GetVulnerabilityDescription(fbBuilder.GetBufferPointer())->scoreBase(), 2));

    EXPECT_STREQ(alert.at("vulnerability").at("enumeration").get_ref<const std::string&>().c_str(), "CVE");
    EXPECT_STREQ(alert.at("vulnerability").at("published").get_ref<const std::string&>().c_str(),
                 GetVulnerabilityDescription(fbBuilder.GetBufferPointer())->datePublished()->c_str());
    EXPECT_STREQ(alert.at("vulnerability").at("reference").get_ref<const std::string&>().c_str(),
                 GetVulnerabilityDescription(fbBuilder.GetBufferPointer())->reference()->c_str());
    EXPECT_STREQ(
        alert.at("vulnerability").at("severity").get_ref<const std::string&>().c_str(),
        Utils::toSentenceCase(GetVulnerabilityDescription(fbBuilder.GetBufferPointer())->severity()->str()).c_str());
    EXPECT_STREQ(alert.at("vulnerability").at("status").get_ref<const std::string&>().c_str(), "Active");

    EXPECT_STREQ(alert.at("vulnerability").at("title").get_ref<const std::string&>().c_str(),
                 (CVEID + " affects " + scanContext->osName().data()).c_str());
    EXPECT_STREQ(alert.at("vulnerability").at("type").get_ref<const std::string&>().c_str(), "Packages");
    EXPECT_STREQ(alert.at("vulnerability").at("updated").get_ref<const std::string&>().c_str(),
                 GetVulnerabilityDescription(fbBuilder.GetBufferPointer())->dateUpdated()->c_str());
}

TEST_F(ScanOsAlertDetailsBuilderTest, TestSuccessfulScanOsAlertWasSolved)
{
    flatbuffers::FlatBufferBuilder fbBuilder;
    auto vulnerabilityDescriptionData =
        NSVulnerabilityScanner::CreateVulnerabilityDescriptionDirect(fbBuilder,
                                                                     "accessComplexity_test_string",
                                                                     "assignerShortName_test_string",
                                                                     "attackVector_test_string",
                                                                     "authentication_test_string",
                                                                     "availabilityImpact_test_string",
                                                                     "classification_test_string",
                                                                     "confidentialityImpact_test_string",
                                                                     "cweId_test_string",
                                                                     "datePublished_test_string",
                                                                     "dateUpdated_test_string",
                                                                     "description_test_string",
                                                                     "integrityImpact_test_string",
                                                                     "privilegesRequired_test_string",
                                                                     "reference_test_string",
                                                                     "scope_test_string",
                                                                     8.3,
                                                                     "3",
                                                                     "severity_test_string",
                                                                     "userInteraction_test_string");
    fbBuilder.Finish(vulnerabilityDescriptionData);

    auto dbWrapper = std::make_unique<Utils::RocksDBWrapper>(TEST_DESCRIPTION_DATABASE_PATH);
    rocksdb::Slice dbValue(reinterpret_cast<const char*>(fbBuilder.GetBufferPointer()), fbBuilder.GetSize());
    if (!dbWrapper->columnExists(DESCRIPTIONS_COLUMN_DEFAULT))
    {
        dbWrapper->createColumn(DESCRIPTIONS_COLUMN_DEFAULT);
    }
    dbWrapper->put(CVEID, dbValue, DESCRIPTIONS_COLUMN_DEFAULT);

    expectOsData();

    spRemediationDataCacheMock = std::make_shared<MockRemediationDataCache>();
    EXPECT_CALL(*spRemediationDataCacheMock, getRemediationData(_)).WillRepeatedly(testing::Return(Remediation {}));

    auto spDatabaseFeedManagerMock = std::make_shared<MockDatabaseFeedManager>();
    EXPECT_CALL(*spDatabaseFeedManagerMock, getVulnerabilityDescriptiveInformation(_, _, _))
        .WillRepeatedly(testing::Invoke(
            [&](const std::string& cveId,
                const std::string& shortName,
                FlatbufferDataPair<NSVulnerabilityScanner::VulnerabilityDescription>& resultContainer)
            {
                dbWrapper->get(std::string(cveId), resultContainer.slice, DESCRIPTIONS_COLUMN_DEFAULT);
                if (flatbuffers::Verifier verifier(reinterpret_cast<const uint8_t*>(resultContainer.slice.data()),
                                                   resultContainer.slice.size());
                    NSVulnerabilityScanner::VerifyVulnerabilityDescriptionBuffer(verifier) == false)
                {
                    resultContainer.data = nullptr;
                    return false;
                }
                resultContainer.data = const_cast<NSVulnerabilityScanner::VulnerabilityDescription*>(
                    NSVulnerabilityScanner::GetVulnerabilityDescription(resultContainer.slice.data()));
                return true;
            }));

    flatbuffers::Parser parser;
    ASSERT_TRUE(parser.Parse(rsync_SCHEMA));
    ASSERT_TRUE(parser.Parse(OS_SCAN_MSG.c_str()));
    uint8_t* buffer = parser.builder_.GetBufferPointer();
    std::variant<const SyscollectorDeltas::Delta*, const Synchronization::SyncMsg*, const nlohmann::json*>
        syscollectorDelta = Synchronization::GetSyncMsg(reinterpret_cast<const char*>(buffer));
    auto scanContext =
        std::make_shared<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>(
            syscollectorDelta);
    scanContext->m_elements[CVEID] = R"({"operation":"DELETED"})"_json;
    scanContext->m_isFirstScan = false;
    scanContext->m_matchConditions[CVEID] = {"version", MatchRuleCondition::LessThan};

    spGlobalDataMock = std::make_shared<MockGlobalData>();
    EXPECT_CALL(*spGlobalDataMock, vendorMaps()).WillRepeatedly(testing::ReturnRef(ADP_DESCRIPTIONS));

    TScanOsAlertDetailsBuilder<MockDatabaseFeedManager,
                               TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>,
                               TrampolineGlobalData>
        scanOsAlertDetailsAugmentation(spDatabaseFeedManagerMock);

    EXPECT_NO_THROW(scanOsAlertDetailsAugmentation.handleRequest(scanContext));

    EXPECT_EQ(scanContext->m_elements.size(), 1);
    EXPECT_NE(scanContext->m_elements.find(CVEID), scanContext->m_elements.end());

    EXPECT_EQ(scanContext->m_alerts.size(), 1);
    EXPECT_NE(scanContext->m_alerts.find(CVEID), scanContext->m_alerts.end());

    auto& alert = scanContext->m_alerts[CVEID];

    std::string alertScoreVersion {"cvss3"};

    EXPECT_STREQ(alert.at("vulnerability").at("cve").get_ref<const std::string&>().c_str(), CVEID.c_str());
    EXPECT_STREQ(alert.at("vulnerability").at("scanner").at("reference").get_ref<const std::string&>().c_str(),
                 CTI_CVE_URL.c_str());

    EXPECT_DOUBLE_EQ(
        alert.at("vulnerability").at("cvss").at(alertScoreVersion).at("base_score").get_ref<const double&>(),
        Utils::floatToDoubleRound(GetVulnerabilityDescription(fbBuilder.GetBufferPointer())->scoreBase(), 2));

    EXPECT_STREQ(alert.at("vulnerability").at("enumeration").get_ref<const std::string&>().c_str(), "CVE");
    EXPECT_STREQ(alert.at("vulnerability").at("published").get_ref<const std::string&>().c_str(),
                 GetVulnerabilityDescription(fbBuilder.GetBufferPointer())->datePublished()->c_str());
    EXPECT_STREQ(alert.at("vulnerability").at("reference").get_ref<const std::string&>().c_str(),
                 GetVulnerabilityDescription(fbBuilder.GetBufferPointer())->reference()->c_str());
    EXPECT_STREQ(
        alert.at("vulnerability").at("severity").get_ref<const std::string&>().c_str(),
        Utils::toSentenceCase(GetVulnerabilityDescription(fbBuilder.GetBufferPointer())->severity()->str()).c_str());
    EXPECT_STREQ(alert.at("vulnerability").at("status").get_ref<const std::string&>().c_str(), "Solved");

    EXPECT_STREQ(alert.at("vulnerability").at("title").get_ref<const std::string&>().c_str(),
                 (CVEID + " affecting " + scanContext->osName().data() + " was solved").c_str());
    EXPECT_STREQ(alert.at("vulnerability").at("type").get_ref<const std::string&>().c_str(), "Packages");
    EXPECT_STREQ(alert.at("vulnerability").at("updated").get_ref<const std::string&>().c_str(),
                 GetVulnerabilityDescription(fbBuilder.GetBufferPointer())->dateUpdated()->c_str());
}

TEST_F(ScanOsAlertDetailsBuilderTest, TestFirstScanNoAlerts)
{
    flatbuffers::FlatBufferBuilder fbBuilder;
    auto vulnerabilityDescriptionData =
        NSVulnerabilityScanner::CreateVulnerabilityDescriptionDirect(fbBuilder,
                                                                     "accessComplexity_test_string",
                                                                     "assignerShortName_test_string",
                                                                     "attackVector_test_string",
                                                                     "authentication_test_string",
                                                                     "availabilityImpact_test_string",
                                                                     "classification_test_string",
                                                                     "confidentialityImpact_test_string",
                                                                     "cweId_test_string",
                                                                     "datePublished_test_string",
                                                                     "dateUpdated_test_string",
                                                                     "description_test_string",
                                                                     "integrityImpact_test_string",
                                                                     "privilegesRequired_test_string",
                                                                     "reference_test_string",
                                                                     "scope_test_string",
                                                                     8.3,
                                                                     "3",
                                                                     "severity_test_string",
                                                                     "userInteraction_test_string");
    fbBuilder.Finish(vulnerabilityDescriptionData);

    auto dbWrapper = std::make_unique<Utils::RocksDBWrapper>(TEST_DESCRIPTION_DATABASE_PATH);
    rocksdb::Slice dbValue(reinterpret_cast<const char*>(fbBuilder.GetBufferPointer()), fbBuilder.GetSize());
    if (!dbWrapper->columnExists(DESCRIPTIONS_COLUMN_DEFAULT))
    {
        dbWrapper->createColumn(DESCRIPTIONS_COLUMN_DEFAULT);
    }
    dbWrapper->put(CVEID, dbValue, DESCRIPTIONS_COLUMN_DEFAULT);

    expectOsData();

    spRemediationDataCacheMock = std::make_shared<MockRemediationDataCache>();
    EXPECT_CALL(*spRemediationDataCacheMock, getRemediationData(_)).WillRepeatedly(testing::Return(Remediation {}));

    auto spDatabaseFeedManagerMock = std::make_shared<MockDatabaseFeedManager>();
    EXPECT_CALL(*spDatabaseFeedManagerMock, getVulnerabilityDescriptiveInformation(_, _, _))
        .WillRepeatedly(testing::Invoke(
            [&](const std::string& cveId,
                const std::string& shortName,
                FlatbufferDataPair<NSVulnerabilityScanner::VulnerabilityDescription>& resultContainer)
            {
                dbWrapper->get(std::string(cveId), resultContainer.slice, DESCRIPTIONS_COLUMN_DEFAULT);
                if (flatbuffers::Verifier verifier(reinterpret_cast<const uint8_t*>(resultContainer.slice.data()),
                                                   resultContainer.slice.size());
                    NSVulnerabilityScanner::VerifyVulnerabilityDescriptionBuffer(verifier) == false)
                {
                    resultContainer.data = nullptr;
                    return false;
                }
                resultContainer.data = const_cast<NSVulnerabilityScanner::VulnerabilityDescription*>(
                    NSVulnerabilityScanner::GetVulnerabilityDescription(resultContainer.slice.data()));
                return true;
            }));

    flatbuffers::Parser parser;
    ASSERT_TRUE(parser.Parse(rsync_SCHEMA));
    ASSERT_TRUE(parser.Parse(OS_SCAN_MSG.c_str()));
    uint8_t* buffer = parser.builder_.GetBufferPointer();
    std::variant<const SyscollectorDeltas::Delta*, const Synchronization::SyncMsg*, const nlohmann::json*>
        syscollectorDelta = Synchronization::GetSyncMsg(reinterpret_cast<const char*>(buffer));
    auto scanContext =
        std::make_shared<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>(
            syscollectorDelta);
    scanContext->m_elements[CVEID] = R"({"operation":"DELETED"})"_json;
    scanContext->m_isFirstScan = true;
    scanContext->m_matchConditions[CVEID] = {"version", MatchRuleCondition::Equal};

    spGlobalDataMock = std::make_shared<MockGlobalData>();
    EXPECT_CALL(*spGlobalDataMock, vendorMaps()).WillRepeatedly(testing::ReturnRef(ADP_DESCRIPTIONS));

    TScanOsAlertDetailsBuilder<MockDatabaseFeedManager,
                               TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>,
                               TrampolineGlobalData>
        scanOsAlertDetailsAugmentation(spDatabaseFeedManagerMock);

    EXPECT_NO_THROW(scanOsAlertDetailsAugmentation.handleRequest(scanContext));

    EXPECT_EQ(scanContext->m_elements.size(), 1);
    EXPECT_NE(scanContext->m_elements.find(CVEID), scanContext->m_elements.end());

    EXPECT_EQ(scanContext->m_alerts.size(), 0);
}

TEST_F(ScanOsAlertDetailsBuilderTest, TestUnknownOperation)
{
    flatbuffers::FlatBufferBuilder fbBuilder;
    auto vulnerabilityDescriptionData =
        NSVulnerabilityScanner::CreateVulnerabilityDescriptionDirect(fbBuilder,
                                                                     "accessComplexity_test_string",
                                                                     "assignerShortName_test_string",
                                                                     "attackVector_test_string",
                                                                     "authentication_test_string",
                                                                     "availabilityImpact_test_string",
                                                                     "classification_test_string",
                                                                     "confidentialityImpact_test_string",
                                                                     "cweId_test_string",
                                                                     "datePublished_test_string",
                                                                     "dateUpdated_test_string",
                                                                     "description_test_string",
                                                                     "integrityImpact_test_string",
                                                                     "privilegesRequired_test_string",
                                                                     "reference_test_string",
                                                                     "scope_test_string",
                                                                     8.3,
                                                                     "3",
                                                                     "severity_test_string",
                                                                     "userInteraction_test_string");
    fbBuilder.Finish(vulnerabilityDescriptionData);

    auto dbWrapper = std::make_unique<Utils::RocksDBWrapper>(TEST_DESCRIPTION_DATABASE_PATH);
    rocksdb::Slice dbValue(reinterpret_cast<const char*>(fbBuilder.GetBufferPointer()), fbBuilder.GetSize());
    if (!dbWrapper->columnExists(DESCRIPTIONS_COLUMN_DEFAULT))
    {
        dbWrapper->createColumn(DESCRIPTIONS_COLUMN_DEFAULT);
    }
    dbWrapper->put(CVEID, dbValue, DESCRIPTIONS_COLUMN_DEFAULT);

    expectOsData();

    spRemediationDataCacheMock = std::make_shared<MockRemediationDataCache>();
    EXPECT_CALL(*spRemediationDataCacheMock, getRemediationData(_)).WillRepeatedly(testing::Return(Remediation {}));

    auto spDatabaseFeedManagerMock = std::make_shared<MockDatabaseFeedManager>();
    EXPECT_CALL(*spDatabaseFeedManagerMock, getVulnerabilityDescriptiveInformation(_, _, _))
        .WillRepeatedly(testing::Invoke(
            [&](const std::string_view cveId,
                const std::string& shortName,
                FlatbufferDataPair<NSVulnerabilityScanner::VulnerabilityDescription>& resultContainer)
            {
                dbWrapper->get(std::string(cveId), resultContainer.slice, DESCRIPTIONS_COLUMN_DEFAULT);
                if (flatbuffers::Verifier verifier(reinterpret_cast<const uint8_t*>(resultContainer.slice.data()),
                                                   resultContainer.slice.size());
                    NSVulnerabilityScanner::VerifyVulnerabilityDescriptionBuffer(verifier) == false)
                {
                    resultContainer.data = nullptr;
                    return false;
                }
                resultContainer.data = const_cast<NSVulnerabilityScanner::VulnerabilityDescription*>(
                    NSVulnerabilityScanner::GetVulnerabilityDescription(resultContainer.slice.data()));
                return true;
            }));

    flatbuffers::Parser parser;
    ASSERT_TRUE(parser.Parse(rsync_SCHEMA));
    ASSERT_TRUE(parser.Parse(OS_SCAN_MSG.c_str()));
    uint8_t* buffer = parser.builder_.GetBufferPointer();
    std::variant<const SyscollectorDeltas::Delta*, const Synchronization::SyncMsg*, const nlohmann::json*>
        syscollectorDelta = Synchronization::GetSyncMsg(reinterpret_cast<const char*>(buffer));
    auto scanContext =
        std::make_shared<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>(
            syscollectorDelta);
    scanContext->m_elements[CVEID] = R"({"operation":"UNKNOWN"})"_json;
    scanContext->m_isFirstScan = false;

    spGlobalDataMock = std::make_shared<MockGlobalData>();
    EXPECT_CALL(*spGlobalDataMock, vendorMaps()).WillRepeatedly(testing::ReturnRef(ADP_DESCRIPTIONS));

    TScanOsAlertDetailsBuilder<MockDatabaseFeedManager,
                               TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>,
                               TrampolineGlobalData>
        scanOsAlertDetailsAugmentation(spDatabaseFeedManagerMock);

    // No throw here because we are catching any possible exception
    scanOsAlertDetailsAugmentation.handleRequest(scanContext);

    EXPECT_EQ(scanContext->m_alerts.size(), 0);
}
